export type TokenState = 'none' | 'yellow' | 'red'

export interface NumberToken {
  type: 'number'
  id: string
  raw: string
  value: number
  precision: number
  state: TokenState
}

export interface TextToken {
  type: 'text'
  id: string
  raw: string
}

export interface LineBreakToken {
  type: 'linebreak'
  id: string
}

export type Token = NumberToken | TextToken | LineBreakToken

export interface ParseOptions {
  allowDecimal: boolean
}

function nextIdGenerator() {
  let counter = 0
  return () => `token-${counter++}`
}

function shouldSkipNumberMatch(
  source: string,
  start: number,
  end: number,
  matched: string,
): boolean {
  const before = start > 0 ? source[start - 1] : ''
  const after = end < source.length ? source[end] : ''
  const lookAhead = source.slice(end, end + 6)

  if (/^\d{1,2}[:：]\d{2}$/.test(matched)) {
    return true
  }

  if (/^\d{1,2}\.\d{1,2}$/.test(matched) && /[~\-更更新]/.test(after + lookAhead)) {
    return true
  }

  if (/^\d{4}$/.test(matched) && after === '年') {
    return true
  }

  if (/^\d{1,2}$/.test(matched)) {
    if (['月', '日', '号', '点', '时'].includes(after) || ['月', '日'].includes(before)) {
      return true
    }
    if (after === ':' || after === '：' || before === ':' || before === '：') {
      return true
    }
    if ((before === '/' || after === '/') && /号|日/.test(lookAhead)) {
      return true
    }
    if (before === '第' && ['场', '天'].includes(after)) {
      return true
    }
  }

  if (before === '<' && lookAhead.includes('>') && /[月日号]/.test(source.slice(start, end + 4))) {
    return true
  }

  return false
}

export function parseTicketText(rawText: string, options: ParseOptions): Token[] {
  const getId = nextIdGenerator()
  const normalized = rawText.replace(/\r\n/g, '\n')
  const regex = options.allowDecimal ? /\d+(?:\.\d+)?/g : /\d+/g
  const tokens: Token[] = []
  let lastIndex = 0
  let match: RegExpExecArray | null

  const pushTextSegment = (segment: string) => {
    if (!segment) {
      return
    }
    const parts = segment.split('\n')
    parts.forEach((part, idx) => {
      if (part) {
        tokens.push({
          type: 'text',
          id: getId(),
          raw: part,
        })
      }
      if (idx < parts.length - 1) {
        tokens.push({
          type: 'linebreak',
          id: getId(),
        })
      }
    })
  }

  while ((match = regex.exec(normalized)) !== null) {
    const [matched] = match
    if (match.index > lastIndex) {
      pushTextSegment(normalized.slice(lastIndex, match.index))
    }
    if (shouldSkipNumberMatch(normalized, match.index, match.index + matched.length, matched)) {
      pushTextSegment(matched)
      lastIndex = regex.lastIndex
      continue
    }
    const precision = matched.includes('.') ? matched.split('.')[1].length : 0
    tokens.push({
      type: 'number',
      id: getId(),
      raw: matched,
      value: Number(matched),
      precision,
      state: 'none',
    })
    lastIndex = regex.lastIndex
  }

  if (lastIndex < normalized.length) {
    pushTextSegment(normalized.slice(lastIndex))
  }

  // Ensure trailing newline generates empty line entry
  if (normalized.endsWith('\n')) {
    tokens.push({
      type: 'linebreak',
      id: getId(),
    })
  }

  return tokens
}

export function tokensToLines(tokens: Token[]): Array<Array<TextToken | NumberToken>> {
  const lines: Array<Array<TextToken | NumberToken>> = []
  let current: Array<TextToken | NumberToken> = []

  tokens.forEach((token) => {
    if (token.type === 'linebreak') {
      lines.push(current)
      current = []
      return
    }
    current.push(token)
  })

  lines.push(current)
  return lines
}

export function formatNumberToken(token: NumberToken): string {
  if (token.precision > 0) {
    return token.value.toFixed(token.precision)
  }
  return String(Math.round(token.value))
}

export function serializeTokens(tokens: Token[]): string {
  return tokens
    .map((token) => {
      if (token.type === 'linebreak') {
        return '\n'
      }
      if (token.type === 'number') {
        return formatNumberToken(token)
      }
      return token.raw
    })
    .join('')
}

export function cloneTokens(tokens: Token[]): Token[] {
  return tokens.map((token) => ({ ...token })) as Token[]
}

export function cycleState(state: TokenState): TokenState {
  switch (state) {
    case 'none':
      return 'yellow'
    case 'yellow':
      return 'red'
    case 'red':
    default:
      return 'none'
  }
}

export function markPriceTokens(tokens: Token[]): void {
  const lines = tokensToLines(tokens)
  lines.forEach((line) => {
    let seenContent = false // 是否出现过非空文本或数字（用于判断行首数字）
    line.forEach((seg) => {
      if (seg.type === 'text') {
        if (seg.raw.trim().length > 0) seenContent = true
        return
      }
      const num = seg as NumberToken
      if (!seenContent) {
        // 行头第一个数字不选中
        num.state = 'none'
        seenContent = true
        return
      }
      num.state = num.value > 99 ? 'yellow' : 'none'
    })
  })
}
